package com.blb.community_admin.config;

import com.blb.community_admin.Annotation.Log;
import com.blb.community_admin.entity.SysLogininfor;
import com.blb.community_admin.entity.SysOperLog;
import com.blb.community_admin.service.ILogininforService;
import com.blb.community_admin.service.IOperationLogService;
import com.blb.community_admin.service.ISysDeptService;
import com.blb.community_admin.service.ISysUserService;
import com.blb.community_admin.util.IpaddrUtil;
import com.blb.community_admin.util.JwtUtil;
import com.blb.community_admin.util.ResponseStatus;
import com.blb.community_admin.util.RsaUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @author hkp
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Resource
    private ILogininforService logininforService;

    @Resource
    private ISysUserService userService;

    @Resource
    private ISysDeptService deptService;

    @Resource
    private IOperationLogService operationLogService;

    /**
     * 给所有的controller的所有方法加日志跟踪
     */
    @Pointcut("execution(* com.blb.community_admin.controller.*Controller.*(..))")
    public void logPointcut() {

    }

    /**
     * 配置环绕通知
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointcut()")
    public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
//        记录方法执行前时间
        long start = System.currentTimeMillis();
//        打印方法名称
        log.info("当前执行方法：" + point.getSignature().getName());
//        打印参数
        Object[] args = point.getArgs();
        for (Object arg : args) {
            log.info("参数：" + arg);
        }
//        打印返回值
        Object result = point.proceed();
        if (log.isDebugEnabled()) {
            log.info("方法返回值：" + result);
        }
//        打印执行时间
        long end = System.currentTimeMillis();
        log.info("方法执行时间：" + (end - start));
        return result;
    }

    /**
     * 切入登录成功的方法
     */
    @Pointcut("execution(* com.blb.community_admin.config.LoginSuccessHandler.onAuthenticationSuccess(..))")
    public void loginSuccessHandlerPointcut() {

    }

    private static SysLogininfor getSysLogininfor(JoinPoint point) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
//        获取User-Agent
        String agent = request.getHeader("User-Agent");
//        解析User-Agent得到userAgent对象
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
//        获取浏览器
        Browser browser = userAgent.getBrowser();
//        获取操作系统对象
        OperatingSystem operatingSystem = userAgent.getOperatingSystem();
        String username = null;
        if (SecurityContextHolder.getContext().getAuthentication() == null){
            username = (String) request.getAttribute("username");
        }else{
            username = SecurityContextHolder.getContext().getAuthentication().getName();
        }
        SysLogininfor sysLogininfor = new SysLogininfor();
        sysLogininfor.setBrowser(browser.getName());
        sysLogininfor.setOs(operatingSystem.getName());
        sysLogininfor.setLoginTime(LocalDateTime.now());
        sysLogininfor.setUserName(username);
        String ipaddr = IpaddrUtil.getIpAddress();
        String loginLocation = IpaddrUtil.getLoginLocation();
        sysLogininfor.setIpaddr(ipaddr);
        sysLogininfor.setLoginLocation(loginLocation);
        return sysLogininfor;
    }

    @After("loginSuccessHandlerPointcut()")
    public void afterLoginSuccessHandler(JoinPoint point) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        SysLogininfor sysLogininfor = getSysLogininfor(point);
        sysLogininfor.setMsg("登录成功");
        sysLogininfor.setStatus("0");
        logininforService.save(sysLogininfor);
    }

    /**
     * 登录失败
     *
     * @param point
     * @throws IOException
     */
    @AfterReturning("execution(* com.blb.community_admin.config.LoginFailureHandler.onAuthenticationFailure(..))")
    public void afterLoginFailure(JoinPoint point) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        SysLogininfor sysLogininfor = getSysLogininfor(point);
        sysLogininfor.setMsg(ResponseStatus.LOGIN_ERROR.getMessage());
        sysLogininfor.setStatus("1");
        logininforService.save(sysLogininfor);
    }

    /**
     * 登出日志
     *
     * @param point
     */
    @AfterReturning("execution(* com.blb.community_admin.config.MyLogoutSuccessHandler.onLogoutSuccess(..))")
    public void afterLogout(JoinPoint point) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        SysLogininfor sysLogininfor = getSysLogininfor(point);
        sysLogininfor.setMsg("登出成功");
        sysLogininfor.setStatus("0");
        logininforService.save(sysLogininfor);
    }

    @AfterReturning(value = "@annotation(com.blb.community_admin.Annotation.Log)", returning = "result")
    public void afterOperate(JoinPoint joinPoint, Object result) throws IOException {
//      操作
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log log = AnnotationUtils.getAnnotation(method, Log.class);
        String title = null;
        Integer businessType = null;
        String requestMethod = null;
        if (log != null) {
            title = log.module();
            businessType = log.operationType();
            requestMethod = log.requestMethod();
        }
        StringBuilder className = new StringBuilder(joinPoint.getTarget().getClass().getName());
        StringBuilder methodName = new StringBuilder(method.getName());
        String method1 = className.append("." + methodName + "()").toString();
        String operUrl = null;
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String token = request.getHeader("Authorization");
        String operName = JwtUtil.getUsernameFromToken(token, RsaUtil.publicKey);
        Long userId = userService.getByName(operName).getUserId();
        String deptName = deptService.getDeptByuserId(userId).getDeptName();
        String operIp = IpaddrUtil.getIpAddress();
        String operLocation = IpaddrUtil.getLoginLocation();
        Object[] args = joinPoint.getArgs();
        ObjectMapper objectMapper = new ObjectMapper();
        String operParam = null;
        String jsonResult = null;
        if (businessType != 5) {
            operParam = objectMapper.writeValueAsString(args);
            jsonResult = objectMapper.writeValueAsString(result);
        } else {
            operParam = objectMapper.writeValueAsString(signature.getParameterNames());
            jsonResult = objectMapper.writeValueAsString("");
        }
        Integer status = 0;
        String errorMsg = null;
        LocalDateTime operTime = LocalDateTime.now();
        GetMapping get = AnnotationUtils.getAnnotation(method, GetMapping.class);
        if (get != null) {
            operUrl = get.value()[0];
        }
        PutMapping put = AnnotationUtils.getAnnotation(method, PutMapping.class);
        if (put != null) {
            operUrl = put.value()[0];
        }
        PostMapping post = AnnotationUtils.getAnnotation(method, PostMapping.class);
        if (post != null) {
            operUrl = post.value()[0];
        }
        DeleteMapping delete = AnnotationUtils.getAnnotation(method, DeleteMapping.class);
        if (delete != null) {
            operUrl = delete.value()[0];
        }
        SysOperLog sysOperLog = new SysOperLog(0L, title, businessType, method1, requestMethod, 1L, operName, deptName, operUrl, operIp, operLocation, operParam, jsonResult, status, errorMsg, operTime);
        operationLogService.save(sysOperLog);
    }
}
